package Seletores;

import java.util.ArrayList;
import java.util.HashMap;

import Util.AMUtil;

public class BaseProbabilidadesDiscreta {
	
	private ArrayList<String[]> base;
	private HashMap<Integer, ArrayList<String>> histogramaKeys;
	private HashMap<Integer, ArrayList<Double>> histograma;
	private HashMap<String, Double> probabilidades;
	private double[] entropia;
//	private HashMap<String, Double> infMutuaCondicional;
	private HashMap<String, Double> infMutua;
	
	public BaseProbabilidadesDiscreta(ArrayList<String[]> base){
		this.base = base;
		this.histogramaKeys = new HashMap<Integer, ArrayList<String>>();
		this.histograma = new HashMap<Integer, ArrayList<Double>>();
		this.probabilidades = new HashMap<String, Double>();
		this.entropia = new double[base.get(0).length];
//		this.infMutuaCondicional = new HashMap<String, Double>();
		this.infMutua = new HashMap<String, Double>();
	}

	public ArrayList<String> getHistogramaKeys(int feature){
		if( !histogramaKeys.containsKey(feature) ){
			histogramaKeys.put(feature, AMUtil.histogramaKeys(base, feature));
			System.gc();
		}
		return histogramaKeys.get(feature);
	}
	
	public ArrayList<Double> getHistograma(int feature){
		if( !histograma.containsKey(feature) ){
			histograma.put(feature, AMUtil.histograma(base, feature));
			System.gc();
		}
		return histograma.get(feature);
	}
	
	public double probabilidade(int feature, String value){
		String key = "f:"+feature+"v:"+value;
		if( !probabilidades.containsKey( key ) ){
			probabilidades.put(key , AMUtil.probabilidade(base, feature, value) );
			System.gc();
		}
		return probabilidades.get(key);
	}
	
	public double probabilidade(int feature1, String value1, int feature2, String value2){
		String key = "f:"+feature1+"v:"+value1+"f:"+feature2+"v:"+value2;
		
		if( !probabilidades.containsKey( key ) ){
			double prob = AMUtil.probabilidadeConjunta(base, feature1, value1, feature2, value2);
			String key2 = "f:"+feature2+"v:"+value2+"f:"+feature1+"v:"+value1;
			probabilidades.put(key , prob);
			probabilidades.put(key2 , prob);
			System.gc();
		}
		return probabilidades.get(key);
	}
	
	public double probabilidade(int feature1, String value1, int feature2, String value2, int feature3, String value3){
		String key = "f:"+feature1+"v:"+value1
					+"f:"+feature2+"v:"+value2
					+"f:"+feature3+"v:"+value3;
		if( !probabilidades.containsKey( key ) ){
			double prob = AMUtil.probabilidadeConjunta(base, feature1, value1, feature2, value2, feature3, value3);
			String key2 = "f:"+feature1+"v:"+value1
						+"f:"+feature3+"v:"+value3
						+"f:"+feature2+"v:"+value2;
			String key3 = "f:"+feature2+"v:"+value2
						+"f:"+feature1+"v:"+value1
						+"f:"+feature3+"v:"+value3;
			String key4 = "f:"+feature2+"v:"+value2
						+"f:"+feature3+"v:"+value3
						+"f:"+feature1+"v:"+value1;
			String key5 = "f:"+feature3+"v:"+value3
						+"f:"+feature1+"v:"+value1
						+"f:"+feature2+"v:"+value2;
			String key6 = "f:"+feature3+"v:"+value3
						+"f:"+feature2+"v:"+value2
						+"f:"+feature1+"v:"+value1;
			
			probabilidades.put(key ,  prob);
			probabilidades.put(key2 ,  prob);
			probabilidades.put(key3 ,  prob);
			probabilidades.put(key4 ,  prob);
			probabilidades.put(key5 ,  prob);
			probabilidades.put(key6 ,  prob);
			System.gc();
		}
		return probabilidades.get(key);
	}
	
	public double probabilidadeCondicional(int feature, String value, int featureCondicao, String valueCondicao ){
		String key = "f:"+feature+"v:"+value+"fc:"+featureCondicao+"vc:"+valueCondicao;
		if( !probabilidades.containsKey( key ) ){
			probabilidades.put(key , AMUtil.probabilidadeCondicional(base, feature, value, featureCondicao, valueCondicao) );
			System.gc();
		}
	
		return probabilidades.get(key);
	}
	
	public double probabilidadeCondicional(int feature1, String value1, int feature2, String value2, int featureCondicao, String valueCondicao ){
		String key = "f:"+feature1+"v:"+value1+"f:"+feature2+"v:"+value2+"fc:"+featureCondicao+"vc:"+valueCondicao;
		if( !probabilidades.containsKey( key ) ){
			double prob = AMUtil.probabilidadeConjuntaCondicional(base, feature1, value1, feature2, value2, featureCondicao, valueCondicao) ;
			String key2 = "f:"+feature1+"v:"+value1+"f:"+feature2+"v:"+value2+"fc:"+featureCondicao+"vc:"+valueCondicao;
			probabilidades.put(key , prob);
			probabilidades.put(key2 , prob);
			System.gc();
		}
		
		return probabilidades.get(key);
	}
	
	public double getEntropia(int feature){
		if(entropia[feature] == 0){
			double entropiaTemp = 0;
			ArrayList<Double> histograma = getHistograma(feature);
			for (Double pAmostra : histograma) {
				entropiaTemp += pAmostra * (Math.log(pAmostra)/Math.log(2));
			}
			entropia[feature] = entropiaTemp;
			System.gc();
		}
		return entropia[feature];
	}
	
	
	public double getInformacaoMutua(int feature1, int feature2){
		String key = "f:"+feature1+"f:"+feature2;
		if(!infMutua.containsKey(key)){
			String key2 = "f:"+feature2+"f:"+feature1;
			double infMutuaTemp = calculaInfMutua(feature1, feature2);
			infMutua.put(key, infMutuaTemp);
			infMutua.put(key2, infMutuaTemp);
			System.gc();
		}
		
		return infMutua.get(key);
	}

	private double calculaInfMutua(int feature1, int feature2){
		
		ArrayList<String> histF1 = getHistogramaKeys(feature1); 
		ArrayList<String> histF2 = getHistogramaKeys(feature2);
		
		double infMutuaTemp = 0;
		
		for (String a1 : histF1) {
			double px = probabilidade(feature1, a1);
			for (String a2 : histF2) {
				double pxy = probabilidade(feature1, a1, feature2, a2);
				if(pxy != 0){
					double py = probabilidade(feature2, a2);
					infMutuaTemp += pxy * Math.log( pxy / (px * py) )/Math.log(2);
//					infMutuaTemp += pxy * Math.log( pxy / (px * py) );
				}
			}
		}
		System.gc();
		return infMutuaTemp;
	}
	
	public double getInformacaoMutuaCondicional(int feature1, int feature2, int featureClasse){
//		String key = "f:" + feature1 + "f:" + "c:" + featureClasse; //estava assim antes
//		String key = "f:" + feature1 + "f:" + feature2 + "c:" + featureClasse;
//		if( !infMutuaCondicional.containsKey(key) ){
			return calcularInformacaoMutuaCondicional(feature1, feature2, featureClasse);
//			infMutuaCondicional.put(key, infMutCond);
//		}
//		if( infMutuaCondicional.size() % 10 == 0 )System.out.println("tamanho da hash infCondMutua: " + infMutuaCondicional.size());
//		return infMutuaCondicional.get(key);
	}
	
	private double calcularInformacaoMutuaCondicional(int feature1, int feature2, int featureClasse ){
		
		ArrayList<String> histF1 = getHistogramaKeys(feature1); 
		ArrayList<String> histF2 = getHistogramaKeys(feature2);
		ArrayList<String> histFClasse = getHistogramaKeys(featureClasse);
		
		double infMutuaCondicional = 0;
		
		for (String value1 : histF1) {
			for (String value2 : histF2) {
				double pxixj = probabilidadeCondicional(feature1, value1, feature2, value2);
				for (String valueClasse : histFClasse) {
					double pxyc = probabilidade(feature1, value1, feature2, value2, featureClasse, valueClasse);
					double pxyx = probabilidadeCondicional(feature1, value1, featureClasse, valueClasse, feature2, value2);
					double pyx = probabilidadeCondicional(featureClasse, valueClasse, feature2, value2);
					
					if(pxixj * pxyc * pxyx * pyx != 0){
						infMutuaCondicional += pxyc * (  Math.log( pxyx / ( pxixj * pyx) ) ) / Math.log(2);
					}
					
				}
			}	
		}

		System.gc();
		
		return infMutuaCondicional;
		
	}
	
	public ArrayList<String[]> getBase() {
		return base;
	}
	
}
